#include <stdlib.h>
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <avr/pgmspace.h>

#include "usbn2mc.h"
#define F_CPU 16000000UL
#include <util/delay.h>

#include "../usbn2mc/main/usbnapi.h"
#include "usbn2mc.h"

#include "common.h"
#include "protocol.h"
#include "wait.h"

#include "93c46.h"
#include "adc.h"
#include "can.h"
#include "eeprom.h"
#include "i2c.h"
#include "io.h"
#include "pwm.h"
#include "spi.h"
#include "uart.h"

#include <stdio.h>
#include "debug.h"


void USBCommandRX (char *buf)
{
  int i;

  debugf("receiving command: %X\n", buf[1]);

  /* a paket can be max 255 bytes */
  if (octopus.long_rx_cmd == 1)
  {
    debugf("get next\n");

    for (i = 0; i < 64; i++)
      request[octopus.long_rx_index + i] = buf[i];

    octopus.long_rx_index = octopus.long_rx_index + i;

    if (octopus.long_rx_index >= octopus.long_rx_bytes)
    {
      octopus.long_rx_cmd = 0;
      USBMessageIn ((char *)request);
    }
  }
  else
  {
    debugf("get and execute\n");

    octopus.long_rx_index = 0;
    for (i = 0; i < 64; i++)
      request[octopus.long_rx_index + i] = buf[i];

    octopus.long_rx_index = i;

    if ((unsigned int) buf[1] < 64)
    {
      octopus.long_rx_cmd = 0;
      USBMessageIn ((char *)request);
    }
    else
    {
      octopus.long_rx_cmd = 1;
      octopus.long_rx_bytes = (unsigned int) buf[1];
    }
  }
}

/* is called when received data from pc */
void USBMessageIn (char *buf)
{
  octopus.long_index = 0;
  octopus.long_running = 0;

  if(octopus.status_led)
    STATUS_LED_on;

  int check = ((int) buf[0] >> 4) & 0x0F;

  debugf("parser, check: %X, command: %X\n", check, buf[0]);

  switch (check)
  {
    case 0:
      if (buf[0] == CMD_EXTERNAL_DEVICE)
      {
        switch (buf[2])
        {
          case CMD_EXTERNAL_93C46:
            flash_93c46_parser(buf);
            break;
        }
      }
      else
        common_parser(buf);
      
      break;

  case 1:
    debugf("calling io_parser\n");
    io_parser (buf);
    break;
  case 2:
    debugf("calling adc_parser\n");
    adc_parser (buf);
    break;
  case 3:
    debugf("calling i2c_parser\n");
    i2c_parser (buf);
    break;
  case 4:
    debugf("calling spi_parser\n");
    spi_parser (buf);
    break;
  case 5:
    debugf("calling pwm_parser\n");
    pwm_parser (buf);
    break;
  case 6:
    debugf("calling adc_parser\n");
    uart_parser (buf);
    break;
  case 7:
#ifdef OCTOPUS_CAN
    debugf("calling can_parser\n");
    can_parser ((uint8_t *)buf);
#else
    debugf("can't call can_parser - no CAN-support\n");
#endif
    break;
  case 8:
    debugf("calling eeprom_parser\n");
    eeprom_parser(buf);
    break;
  default:
    debugf("unknown cmd\n");
    answer[0] = buf[0];
    answer[1] = RSP_UNKOWN_CMD;
    answer[2] = 0;
    CommandAnswer(3);
  }

  if(octopus.status_led)
    STATUS_LED_off;
}


unsigned int strlen(volatile unsigned char *str)
{
  unsigned int len = 0;
   
  while (*str++)
    len++;
   
  return len;
}


void CommandAnswer(unsigned int length)
{
  int i;

  // if first packet of a long message
  if (length > 64 && octopus.long_running == 0)
  {
    debugf("first packet of a long message (length: %d)\n", length);
    octopus.long_index = 0;
    octopus.long_bytes = length;
    octopus.long_running = 1;
    length = 64;
  }

  USBNWrite (FIFOTXC1, FLUSH);

  for (i = 0; i < length; i++)
    USBNWrite (TXD1, answer[octopus.long_index + i]);

  /* control togl bit */
  if(octopus.datatogl)
  {
    USBNWrite (FIFOTXC1, TX_LAST + TX_EN + TX_TOGL);
    octopus.datatogl = 0;
  }
  else
  {
    USBNWrite (FIFOTXC1, TX_LAST + TX_EN);
    octopus.datatogl = 1;
  }
}


void CommandAnswerRest (void)
{
  if (octopus.long_running == 1)
  {
    debugf("octopus long running message\n");
    if (octopus.long_index < octopus.long_bytes)
    {
      debugf("octopus.long_index < octopus.long_bytes\n");
      int diff = octopus.long_bytes - octopus.long_index;
      octopus.long_index = octopus.long_index + 64;

      if (diff > 64)
      {
        debugf("not last packet: diff > 64\n");
        CommandAnswer (64);
      }
      else
      {
        debugf("last packet\n");
        /* last packet */
        CommandAnswer (diff);
        octopus.long_running = 0;
      }
    }
  }
}


int main(void)
{

  USBNInitMC ();
  USBNStartClock();
  
  DDRB  = 0xFF;
  PORTB = 0x00;

  /* use status led */
  octopus.status_led = 1;

  int interf;
  int conf;

  STATUS_LED_off;

  USBNInit ();

  // setup your device
  USBNDeviceVendorID (0x1781);  // 0x0400 is the number from national
  USBNDeviceProductID (0x0c65); // add your product id
  USBNDeviceBCDDevice (0x0001); // you can use it as version e.g. version 1.02 

  char lang[] = { 0x09, 0x04, 0x00 };
  _USBNAddStringDescriptor (lang);  // language descriptor


  /* Attention!!! Descriptors  must be a factor of 8 (error in the stack) */
  USBNDeviceManufacture ("EmbeddedProjects");
  USBNDeviceProduct ("OctopusUSB Interface Converter and I/O Extension");
  //USBNDeviceProduct ("Octopus USB v3.3");
  USBNDeviceSerialNumber ("20090718");

  conf = USBNAddConfiguration ();

  // we reserve 500mA, because users could drive LEDs and need more power
  USBNConfigurationPower (conf, 200);

  interf = USBNAddInterface (conf, 0);
  USBNAlternateSetting (conf, interf, 0);

  /* communication */
  USBNAddInEndpoint (conf, interf, 1, 0x01, BULK, 64, 0, &CommandAnswerRest);
  USBNAddOutEndpoint (conf, interf, 1, 0x01, BULK, 64, 0, &USBCommandRX);
  //USBNAddOutEndpoint (conf, interf, 1, 0x02, BULK, 64, 0, NULL);

  octopus.datatogl = 0;
  octopus.long_rx_cmd = 0;

  /* UARTInit2(38400,8,'N',1); */
  debug_init();

  /* hello world led pattern */
  DDRC  = 0x7F;
  PORTC = 0x2A;
  delay_250ms ();
  PORTC = 0x55;
  delay_250ms ();
  PORTC = 0x00;

  common_init();

  /* init connection between avr and usbn9604 */
  USBNInitMC ();

  /* start usb chip */
  USBNStart (&octopus.datatogl);
  sei();

  while (1)
  {
    USBNInterrupt ();
  }

  return 0;
}

